/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#if !defined(AFX_GDIPHELPER_H__5DC213F6_6231_4FE9_BA73_21FDB7D3992F__INCLUDED_)
#define AFX_GDIPHELPER_H__5DC213F6_6231_4FE9_BA73_21FDB7D3992F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <afxmt.h>
#include <pgl/igfx/STLIGfx.h>
#include <pgl/igfx/GDIpInterface.h>

namespace gfxinterface
{
using namespace Gdiplus;

/*! \brief A class wrapper for GDI+ initialization

 Author: Ryan Johnston, see http://www.codeproject.com/vcpp/gdiplus/GDIPlusControl.asp
 \ingroup GDIpGroup
*/
class CGDIpInitializer 
{
public:
	//! \name Constructors
	//@{
	//! Constructor offers the ability to initialize on construction, or delay until needed.
	CGDIpInitializer(bool bInitCtorDtor = false);
	//! If GDI+ has not explicitly been Deinitialized, do it in the destructor
	virtual ~CGDIpInitializer() ;
	//@}

	//! \name Initialisation and deinitialisation of GDI+
	//@{
	/*! \brief Initialize function, makes sure only one GDI+ is called for each process.
	
	  This function creates a file mapping based on the current process id.
	If the mapping already exists, it knows that another instance of this class
	elsewhere in the process has already taken care of starting GDI+.
	*/
	bool Initialize();

	/*! \brief Deinitialize or decrease initialize count.
	
	  No tricks to this function.  If this was the class that originally started GDI+,
	 and its initialization count has reached zero, it shuts down GDI+.
	 */
	void Deinitialize();
	//! Sets dtor cleaning.
	void SetInitCtorDtor(bool bEnable = true)	{	m_bInitCtorDtor;};
	//@}

	//! \name State
	//@{
	//! returns true if GDI+ successfuly initialized
	bool IsInitialized() const					{	return m_bInited && (m_sGDIpInitedWithSuccess == Gdiplus::Ok);};
	//@}

private:
	HANDLE							m_hMap;
	bool							m_bInited;
	bool							m_bInitCtorDtor;
	ULONG_PTR						m_gdiplusToken;
	Gdiplus::GdiplusStartupInput	m_gdiplusStartupInput;
	static long						m_initcount;
	Gdiplus::Status					m_sGDIpInitedWithSuccess;
};

/*! \brief Class wrapper for double buffering using Gdiplus

	The class is thread safe.
	\ingroup GDIpGroup
*/
class CGDIpHelper  
{
public:
    typedef boost::scoped_ptr<Gdiplus::CachedBitmap> cached_bitmap_ptr;
    typedef boost::scoped_ptr<Gdiplus::Bitmap> bitmap_ptr;
    typedef boost::scoped_ptr<Gdiplus::Graphics> graphics_ptr;

public:
	//! \name Constructors
	//@{
	//! default constructor
	CGDIpHelper(CGDIpInterface* pGfx = NULL);
	/*! desctructor
	Memory bitmap, graphic and cached bitmap are deleted.
	*/
	virtual ~CGDIpHelper();
	//@}

	//! \name Main draw method
	//@{
	void Draw(const CRect& rect,  HDC hDC, Graphics *pGraphics);
	//@}


	//! \name 
	//@{
	//! enables triple buffering
	void SetTripleBuffering(bool yes = true)	{	m_bTripleBuffering = yes; SetDirty();};
	//! get triple buffering state
	bool IsTripleBuffering() const				{	return m_bTripleBuffering;};
	//! Makes rebuild all buffers (back, front, cached)
	void SetDirty();
	//! Makes rebuild front and cached buffer  
	void SetFrontDirty();
	//! Makes rebuild cached buffer
	void SetCacheDirty();
	//! return true if cache bitmap is dirty and has to be rebuild
	bool IsCacheDirty() const					{	return m_pCachedBitmap;};
	//! Destroys all buffers
	void Destroy();
	//@}

	//! \name GDI+ interface
	//@{
	//! sets current GDI+ interface
	void SetGfxInterface(CGDIpInterface* pGfx)	{	m_pGfx=pGfx;};
	//! returns current GDI+ interface
	CGDIpInterface*	GetGfxInterface()			{	return m_pGfx;};
	//@}

	//! \name Bitmap access
	//@{
	//! return offscreen GDIp Graphics object
	Graphics*		GetMemGraphics()			{	return m_pMemGraphics.get();};
	//! return offscreen bitmap
	Bitmap*			GetMemBitmap()				{	return m_pMemBitmap.get();};
	//@}


	//! \name Static function helpers
	//@{
	//! Getting the list of installed encoder codecs
	static void GetImageEncodersList( std::vector<std::_tstring>& encoders );
	//! Getting the list of installed decoder codecs
	static void GetImageDecodersList( std::vector<std::_tstring>& encoders );
	//! get encoder Clsid
	static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
	//! returns the string corresponding to the status
	static void GetStatusString(Gdiplus::Status status, std::_tstring& strStatus, std::_tstring& strDescription);
	//@}

protected:
	//! \name Virtual override
	//@{
	//! Draws to memory bitmap
	virtual void DrawOffScreen(ELayer eLayer) = 0;
	//@}

	//! \name Methods
	//@{
	/*! Creates memory bitmap
	\sa DrawOffScreen
	*/
	void CreateOffScreenBitmap(int nWidth, int nHeight, HDC hDC, Graphics* pGraphics, ELayer eLayer);
	/*! Creates cached bitmap from memory bitmap
	\sa CreateOffScreenBitmap
	*/
	void CreateCachedBitmap(Graphics* pGraphics);
	//@}

	//! \name Attributes
	//@{
	//! cached offscreen bitmap into which all the drawing goes
	cached_bitmap_ptr	m_pCachedBitmap; 
	//! memory offscreen bitmap
	bitmap_ptr			m_pMemBitmap;
	//! memory offscreen bitmap, front
	bitmap_ptr			m_pMemBitmapFront;
	//! memory graphics associated to m_pMemBitmap
	graphics_ptr		m_pMemGraphics; 
	//! current GDI+ interface
	CGDIpInterface*	m_pGfx;
	//! triple buffering bit
	bool m_bTripleBuffering;
	//! multi-threading
	CMutex m_ThreadLock;
	//@}
};



};
#endif // !defined(AFX_GDIPHELPER_H__5DC213F6_6231_4FE9_BA73_21FDB7D3992F__INCLUDED_)
